package com.klw.business.service.impl;

import com.klw.business.domain.KlwMlFeatures;
import com.klw.business.domain.KlwWarningResults;
import com.klw.business.mapper.KlwMlFeaturesMapper;
import com.klw.business.mapper.KlwWarningResultsMapper;
import com.klw.business.service.IMLPredictionService;
import com.klw.business.utils.MLModelUtils;
import com.klw.common.config.RuoYiConfig;
import com.klw.common.core.domain.entity.SysUser;
import com.klw.common.utils.SecurityUtils;
import com.klw.system.service.ISysDictDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import weka.classifiers.Classifier;
import weka.classifiers.trees.RandomForest;
import weka.core.Instance;
import weka.core.Instances;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Formatter;
import java.util.List;
/**
 * 基于随机森林算法的学业警示预测服务实现
 */
@Service
public class MLPredictionServiceImpl implements IMLPredictionService {

    @Autowired
    private KlwMlFeaturesMapper mlFeaturesMapper;

    @Autowired
    private KlwWarningResultsMapper warningResultsMapper;
    
    @Autowired
    private ISysDictDataService sysDictDataService;

    private static final String DICT_TYPE = "sys_warning_level";

    // 随机森林分类器实例
    private Classifier classifier;

    /**
     * 系统启动时加载模型，若模型不存在则训练新模型
     */
    @PostConstruct
    public void init() {
        try {
            // 尝试加载已存在的模型
            classifier = MLModelUtils.loadModel(RuoYiConfig.getModelPath());
            System.out.println("模型加载成功");
        } catch (Exception e) {
            // 模型加载失败，训练新模型
            try {
                // 先检查是否有足够的带标签数据
                long labeledDataCount = countLabeledTrainingData();
                if (labeledDataCount < 10) { // 至少需要10条带标签数据
                    System.out.println("带标签的训练数据不足（至少需要10条），无法训练模型");
                    return;
                }
                System.out.println("模型文件不存在或加载失败，开始训练新模型...");
                trainModel();
                System.out.println("新模型训练完成并已保存");
            } catch (Exception ex) {
                System.err.println("模型训练失败: " + ex.getMessage());
                ex.printStackTrace();
            }
        }
    }

    /**
     * 统计带标签的训练数据数量
     */
    private long countLabeledTrainingData() {

        // 查询所有特征数据
        List<KlwMlFeatures> features = mlFeaturesMapper.selectAll();
        if (features.isEmpty()) {
            return 0;
        }

        // 统计有对应预警结果的数据数量
        long count = 0;
        for (KlwMlFeatures feature : features) {
            System.out.println("studentId:"+feature.getStudentId()+" and semesterId:"+feature.getSemesterId());
            KlwWarningResults result = warningResultsMapper.selectByStudentAndSemester(
                    feature.getStudentId(), feature.getSemesterId());
            if (result != null && result.getWarningLevel() != null) {
                count++;
            }
        }
        System.out.println("count:"+count);
        return count;
    }

    /**
     * 使用随机森林算法训练模型
     */
    @Override
    public void trainModel() throws Exception {
        // 1. 创建数据集结构
        Instances structure = MLModelUtils.createInstancesStructure();

        // 2. 获取训练数据（特征+历史预警结果）
        List<KlwMlFeatures> features = mlFeaturesMapper.selectAll();
        if (features.isEmpty()) {
            throw new Exception("没有找到训练数据，请先导入特征数据");
        }

        Instances trainingData = MLModelUtils.convertToInstances(features, structure);

        // 3. 为每个实例设置类别标签（使用历史预警结果）
        for (int i = 0; i < features.size() && i < trainingData.numInstances(); i++) {
            KlwMlFeatures feature = features.get(i);
            Instance instance = trainingData.instance(i);

            // 从数据库查询历史预警结果作为标签
            KlwWarningResults historyResult = warningResultsMapper.selectByStudentAndSemester(
                    feature.getStudentId(), feature.getSemesterId());

            if (historyResult != null && historyResult.getWarningLevel() != null) {
                MLModelUtils.setClassLabel(instance, historyResult.getWarningLevel());
            } else {
                // 移除没有标签的数据
                trainingData.delete(i);
            }
        }

        if (trainingData.numInstances() == 0) {
            throw new Exception("没有有效的训练数据（缺少标签数据）");
        }

        // 4. 初始化随机森林分类器并设置参数
        RandomForest randomForest = new RandomForest();
        randomForest.setNumIterations(100); // 树的数量
        randomForest.setMaxDepth(0); // 不限制树深度
        randomForest.setSeed(42); // 随机种子，保证结果可复现
        randomForest.setBagSizePercent(100); // 每个树使用的样本比例

        // 5. 训练模型
        randomForest.buildClassifier(trainingData);

        // 6. 保存模型
        MLModelUtils.saveModel(randomForest, RuoYiConfig.getModelPath());

        // 7. 更新当前使用的分类器
        this.classifier = randomForest;
    }

    /**
     * 预测单个学生的预警等级并保存结果
     */
    @Override
    public KlwWarningResults predictAndSave(Long studentId, Long semesterId) throws Exception {
        if (!isModelInitialized()) {
            throw new Exception("模型未初始化，请先训练模型");
        }

        // 1. 获取学生特征数据
        KlwMlFeatures feature = mlFeaturesMapper.selectByStudentAndSemester(studentId, semesterId);
        if (feature == null) {
            throw new Exception("未找到学生 " + studentId + " 在学期 " + semesterId + " 的特征数据");
        }

        // 2. 转换为模型输入格式
        Instances structure = MLModelUtils.createInstancesStructure();
        Instance instance = MLModelUtils.convertToInstance(feature, structure);

        // 3. 使用随机森林模型预测
        double prediction = classifier.classifyInstance(instance);
        String warningLevel = structure.classAttribute().value((int) prediction);

        // 4. 构建预警结果对象
        KlwWarningResults result = new KlwWarningResults();
        result.setStudentId(studentId);
        result.setSemesterId(semesterId);
        result.setWarningLevel(Integer.parseInt(warningLevel));

        // 5. 设置创建/更新信息
        SysUser currentUser = SecurityUtils.getLoginUser().getUser();
        String username = currentUser != null ? currentUser.getUserName() : "system";

        result.setCreateBy(username);
        result.setCreateTime(new Date());
        result.setUpdateBy(username);
        result.setUpdateTime(new Date());
        result.setRemark(sysDictDataService.selectDictLabel(DICT_TYPE,warningLevel));
        result.setAvailablePercent(new BigDecimal(getPredictionConfidence(instance)));

        return result;
    }

    /**
     * 批量预测指定学期所有学生的预警等级
     */
    @Override
    public List<KlwWarningResults> batchPredictAndSave(Long semesterId) throws Exception {
        if (!isModelInitialized()) {
            throw new Exception("模型未初始化，请先训练模型");
        }

        // 1. 获取指定学期的所有学生特征数据
        List<KlwMlFeatures> features = mlFeaturesMapper.selectBySemester(semesterId);
        if (features.isEmpty()) {
            throw new Exception("未找到学期 " + semesterId + " 的任何特征数据");
        }

        // 2. 转换为模型输入格式
        Instances structure = MLModelUtils.createInstancesStructure();
        Instances instances = MLModelUtils.convertToInstances(features, structure);

        // 3. 获取当前登录用户信息
        SysUser currentUser = SecurityUtils.getLoginUser().getUser();
        String username = currentUser != null ? currentUser.getUserName() : "system";
        Date now = new Date();

        // 4. 初始化结果列表
        List<KlwWarningResults> results = new ArrayList<>(features.size());

        // 5. 批量预测并构建结果
        for (int i = 0; i < features.size(); i++) {
            KlwMlFeatures feature = features.get(i);
            Instance instance = instances.instance(i);

            // 预测预警等级
            double prediction = classifier.classifyInstance(instance);
            String warningLevel = structure.classAttribute().value((int) prediction);

            // 创建结果对象
            KlwWarningResults result = new KlwWarningResults();
            result.setStudentId(feature.getStudentId());
            result.setSemesterId(semesterId);
            result.setWarningLevel(Integer.parseInt(warningLevel));
            result.setCreateBy(username);
            result.setCreateTime(now);
            result.setUpdateBy(username);
            result.setUpdateTime(now);
            result.setRemark(sysDictDataService.selectDictLabel(DICT_TYPE,warningLevel));
            result.setAvailablePercent(new BigDecimal(getPredictionConfidence(instance)));

            // 添加到结果列表
            results.add(result);
        }

        return results;
    }

    /**
     * 检查模型是否已初始化
     */
    @Override
    public boolean isModelInitialized() {
        return classifier != null;
    }

    /**
     * 获取预测结果的置信度
     */
    private double getPredictionConfidence(Instance instance) throws Exception {
        if (classifier instanceof RandomForest) {
            double[] distribution = ((RandomForest) classifier).distributionForInstance(instance);
            double maxProbability = 0;
            for (double prob : distribution) {
                if (prob > maxProbability) {
                    maxProbability = prob;
                }
            }
            return maxProbability;
        }
        return 0;
    }
}
